#include "mkdatarobot.h"

#include "EPosixClientSocket.h"
#include "EPosixClientSocketPlatform.h"

#include "def.h"
#include <stdio.h>
#include <sstream>//stringstream

using namespace std;

const int PING_DEADLINE = 2; // seconds
const int SLEEP_BETWEEN_PINGS = 30; // seconds

string getField( TickType field) {
  switch( field) {
    case BID_SIZE:                      return "bidSize";
    case BID:                           return "bidPrice";
    case ASK:                           return "askPrice";
    case ASK_SIZE:                      return "askSize";
    case LAST:                          return "lastPrice";
    case LAST_SIZE:                     return "lastSize";
    case HIGH:                          return "high";
    case LOW:                           return "low";
    case VOLUME:                        return "volume";
    case CLOSE:                         return "close";
    case BID_OPTION_COMPUTATION:        return "bidOptComp";
    case ASK_OPTION_COMPUTATION:        return "askOptComp";
    case LAST_OPTION_COMPUTATION:       return "lastOptComp";
    case MODEL_OPTION:                  return "optionModel";
    case OPEN:                          return "open";
    case LOW_13_WEEK:                   return "13WeekLow";
    case HIGH_13_WEEK:                  return "13WeekHigh";
    case LOW_26_WEEK:                   return "26WeekLow";
    case HIGH_26_WEEK:                  return "26WeekHigh";
    case LOW_52_WEEK:                   return "52WeekLow";
    case HIGH_52_WEEK:                  return "52WeekHigh";
    case AVG_VOLUME:                    return "AvgVolume";
    case OPEN_INTEREST:                 return "OpenInterest";
    case OPTION_HISTORICAL_VOL:         return "OptionHistoricalVolatility";
    case OPTION_IMPLIED_VOL:            return "OptionImpliedVolatility";
    case OPTION_BID_EXCH:               return "OptionBidExchStr";
    case OPTION_ASK_EXCH:               return "OptionAskExchStr";
    case OPTION_CALL_OPEN_INTEREST:     return "OptionCallOpenInterest";
    case OPTION_PUT_OPEN_INTEREST:      return "OptionPutOpenInterest";
    case OPTION_CALL_VOLUME:            return "OptionCallVolume";
    case OPTION_PUT_VOLUME:             return "OptionPutVolume";
    case INDEX_FUTURE_PREMIUM:          return "IndexFuturePremium";
    case BID_EXCH:                      return "bidExch";
    case ASK_EXCH:                      return "askExch";
    case AUCTION_VOLUME:                return "auctionVolume";
    case AUCTION_PRICE:                 return "auctionPrice";
    case AUCTION_IMBALANCE:             return "auctionImbalance";
    case MARK_PRICE:                    return "markPrice";
    case BID_EFP_COMPUTATION:           return "bidEFP";
    case ASK_EFP_COMPUTATION:           return "askEFP";
    case LAST_EFP_COMPUTATION:          return "lastEFP";
    case OPEN_EFP_COMPUTATION:          return "openEFP";
    case HIGH_EFP_COMPUTATION:          return "highEFP";
    case LOW_EFP_COMPUTATION:           return "lowEFP";
    case CLOSE_EFP_COMPUTATION:         return "closeEFP";
    case LAST_TIMESTAMP:                return "lastTimestamp";
    case SHORTABLE:                     return "shortable";
    case FUNDAMENTAL_RATIOS:            return "fundamentals";
    case RT_VOLUME:                     return "RTVolume";
    case HALTED:                        return "halted";
    case BID_YIELD:                     return "bidYield";
    case ASK_YIELD:                     return "askYield";
    case LAST_YIELD:                    return "lastYield";
    case CUST_OPTION_COMPUTATION:       return "custOptComp";
    case TRADE_COUNT:                   return "trades";
    case TRADE_RATE:                    return "trades/min";
    case VOLUME_RATE:                   return "volume/min";
    case LAST_RTH_TRADE:                return "lastRTHTrade";
    default:                            return "unknown";
  }
}

mkdatarobot::mkdatarobot(): m_pClient(new EPosixClientSocket(this)), m_sleepDeadline(0) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}

mkdatarobot::~mkdatarobot() { }

bool mkdatarobot::connect(const char *host, unsigned int port, int clientId) {
  printf("[%s]Connecting to %s:%d clientId:%d\n", __func__,host, port, clientId);
  bool bRes = m_pClient->eConnect( host, port, clientId);
  if (bRes) { printf("[%s]Connected to %s:%d clientId:%d\n", __func__,host, port, clientId); }else{ printf("[%s]Cannot connect to %s:%d clientId:%d\n", __func__, host, port, clientId); }
  return bRes;
}

void mkdatarobot::disconnect() const {
  m_pClient->eDisconnect();
  //printf ("Disconnected\n");
}

bool mkdatarobot::isConnected() const {
  return m_pClient->isConnected();
}

void mkdatarobot::processMessages(){
  fd_set readSet, writeSet, errorSet;
  struct timeval tval;
  tval.tv_usec = 0;
  tval.tv_sec = 0;

  if( m_pClient->fd() >= 0 ) {
    FD_ZERO( &readSet);
    errorSet = writeSet = readSet;

    FD_SET( m_pClient->fd(), &readSet);

    if( !m_pClient->isOutBufferEmpty())
      FD_SET( m_pClient->fd(), &writeSet);

    FD_CLR( m_pClient->fd(), &errorSet);

    int ret = select( m_pClient->fd() + 1, &readSet, &writeSet, &errorSet, &tval);

    if( ret == 0) { // timeout
      return;
    }

    if( ret < 0) {  // error
      disconnect();
      return;
    }

    if( m_pClient->fd() < 0)
      return;

    if( FD_ISSET( m_pClient->fd(), &errorSet)) {
      // error on socket
      m_pClient->onError();
    }

    if( m_pClient->fd() < 0)
      return;

    if( FD_ISSET( m_pClient->fd(), &writeSet)) {
      // socket is ready for writing
      m_pClient->onSend();
    }

    if( m_pClient->fd() < 0)
      return;

    if( FD_ISSET( m_pClient->fd(), &readSet)) {
      // socket is ready for reading
      m_pClient->onReceive();
    }
  }
}

// methods
void mkdatarobot::reqCurrentTime() {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}

void mkdatarobot::ReqMkData(const Contract& contract) {
  //contract.includeExpiry = false;
  //m_state = ST_ReqMkData_ACK;
  string gt="100,101,104,105,106,107,165,221,225,233,236,258,293,294,295,318";
  //printf("m_pClient->reqMktData: %d\n",contract.conId);
  m_pClient->reqMktData(contract.conId, contract, gt, false);//TODO
}

void mkdatarobot::CancelMkData() {
  //printf("Cancelling marketdata %d\n", mid);
  m_pClient->cancelMktData(tickerId);
}

///////////////////////////////////////////////////////////////////
// events
void mkdatarobot::orderStatus(OrderId orderId, const IBString &status, int filled, int remaining,
    double avgFillPrice, int permId, int parentId, double lastFillPrice, int clientId, const IBString& whyHeld)
{
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}

void mkdatarobot::nextValidId(OrderId orderId) { }

void mkdatarobot::currentTime(long time) { }

//Error Code: https://www.interactivebrokers.com/en/software/api/apiguide/tables/api_message_codes.htm
void mkdatarobot::error(const int id, const int errorCode, const IBString errorString){
  //printf("[%s]Error id=%d, errorCode=%d, msg=%s\n", __PRETTY_FUNCTION__ ,id, errorCode, errorString.c_str());
  if( id == -1 && errorCode == 1100) // if "Connectivity between IB and TWS has been lost"
    disconnect();
  if (errorCode==103){
    cancelOrder(id);
  }
}

void mkdatarobot::reqAccountUpdates(bool subscribe){
  m_pClient->reqAccountUpdates(subscribe, account);
}

void mkdatarobot::updateAccountValue(const string &key, const string &val, const string &currency, const string &accountName){
  string dspStr = "key=" + key + " value=" + val + " currency=" + currency + " account=" + accountName;
  if (key=="EquityWithLoanValue"){
    initialmargin = atof(val.c_str());
    printf("[%s]%s\n",__func__,dspStr.c_str());
  }else if(key=="FullAvailableFunds-S"){
    availablefund = atof(val.c_str());
    printf("[%s]%s\n",__func__,dspStr.c_str());
  }
}

void mkdatarobot::updatePortfolio(const Contract& contract, int _position,
    double marketPrice, double marketValue, double averageCost,
    double unrealizedPNL, double realizedPNL, const IBString& accountName)
{
  if (contract.symbol==symbol){
    currentposition = _position;
    char str[512];
    sprintf(str,"conId=%ld,symbol=%s,,secType=%s,,expiry=%s,,strike=%f,right=%s,multiplier=%s,primaryExchange=%s"
                ",currency=%s,localSymbol=%s,position=%d,mktPrice=%f,mktValue=%f,avgCost=%f,unrealizedPNL=%f,realizedPNL=%f,account=%s",
        contract.conId, contract.symbol.c_str(), contract.secType.c_str(), contract.expiry.c_str(),
        contract.strike, contract.right.c_str(), contract.multiplier.c_str(), contract.primaryExchange.c_str(),
        contract.currency.c_str(), contract.localSymbol.c_str(),
        _position, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, accountName.c_str());
    printf("[%s]%s\n",__func__,str);
  }
}

void mkdatarobot::tickPrice(TickerId _tickerId, TickType field, double price, int canAutoExecute) {
  tickerId = _tickerId;
  if (field == BID){
    if (ismaster){
      pdiff->pmaster.bid = price;
    }else{
      pdiff->pslave.bid = price;
    }
    pdiff->updatespread_limit(ismaster, BID, currentposition, spd);//
           //updatespread_limit(ismaster, BID, currentposition, spd);
  }else if (field == ASK){
    if (ismaster){
      pdiff->pmaster.ask = price;
    }else{
      pdiff->pslave.ask = price;
    }
    pdiff->updatespread_limit(ismaster, ASK, currentposition, spd);
  }
  stringstream str;
  str <<"["<< __func__ << "][" << symbol << "]ismaster="<< (ismaster?"YES":"NO") << ", id=" << tickerId <<
    ", " << getField(field) << "=" <<  price << " position: " << currentposition <<
    " canAutoExecute=" << canAutoExecute << endl;
  printf("%s",str.str().c_str());
}

void mkdatarobot::tickGeneric(TickerId tickerId, TickType field, double value) {
  stringstream str;
  str <<"["<< __func__ << "][" << symbol << "]ismaster="<< (ismaster?"YES":"NO") << ", id=" << tickerId <<
    ", " << getField(field) << "=" <<  value << " position: " << currentposition << endl;
  printf("%s",str.str().c_str());
  if(field==LAST_TIMESTAMP){
    cout << getField(field) << endl;
  }else if(field==SHORTABLE){
    cout << getField(field) << endl;
  }
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::tickString(TickerId tickerId, TickType field, const IBString& value) {
  stringstream str;
  str <<"["<< __func__ << "][" << symbol << "]ismaster="<< (ismaster?"YES":"NO") << ", id=" << tickerId <<
    ", " << getField(field) << "=" <<  value << " position: " << currentposition << endl;
  printf("%s",str.str().c_str());
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}

void mkdatarobot::tickSize(TickerId tickerId, TickType field, int size){
  /*stringstream str;
    str << "[" << symbol << "]" << "id=" << tickerId << ", " << getField(field) << "=" << size << endl;
    printf("%s",str.str().c_str());*/
  if (field == BID_SIZE){
    if (ismaster){
      pdiff->pmaster.bidsize = size;
    }else{
      pdiff->pslave.bidsize = size;
    }
  }else if (field == ASK_SIZE){
    if (ismaster){
      pdiff->pmaster.asksize = size;
    }else{
      pdiff->pslave.asksize = size;
    }
  }
}


void mkdatarobot::cancelOrder(int oid){ }
void mkdatarobot::tickOptionComputation( TickerId tickerId, TickType field, double impliedVol, double delta, double optPrice, double pvDividend, double gamma, double vega, double theta, double undPrice) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::tickEFP(TickerId tickerId, TickType field, double basisPoints, const IBString& formattedBasisPoints, double totalDividends, int holdDays, const IBString& futureExpiry, double dividendImpact, double dividendsToExpiry) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::openOrder(OrderId orderId, const Contract&, const Order&, const OrderState&){}
void mkdatarobot::openOrderEnd() {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::winError( const IBString &str, int lastError) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::connectionClosed() {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
/*void mkdatarobot::updateAccountValue(const IBString& key, const IBString& val, const IBString& currency, const IBString& accountName) {
//printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::updatePortfolio(const Contract& contract, int position, double marketPrice, double marketValue, double averageCost, double unrealizedPNL, double realizedPNL, const IBString& accountName){
//printf("[%s]\n",__PRETTY_FUNCTION__);
}*/
void mkdatarobot::updateAccountTime(const IBString& timeStamp) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::accountDownloadEnd(const IBString& accountName) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::contractDetails( int reqId, const ContractDetails& contractDetails) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::bondContractDetails( int reqId, const ContractDetails& contractDetails) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::contractDetailsEnd( int reqId) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::execDetails( int reqId, const Contract& contract, const Execution& execution) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::execDetailsEnd( int reqId) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::updateMktDepth(TickerId id, int position, int operation, int side, double price, int size) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::updateMktDepthL2(TickerId id, int position, IBString marketMaker, int operation, int side, double price, int size) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::updateNewsBulletin(int msgId, int msgType, const IBString& newsMessage, const IBString& originExch) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::managedAccounts( const IBString& accountsList) {
  printf("[clientid=%d]the managed account is:[%s]\n", m_pClient->clientId(), accountsList.c_str());
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::receiveFA(faDataType pFaDataType, const IBString& cxml) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::historicalData(TickerId reqId, const IBString& date, double open, double high, double low, double close, int volume, int barCount, double WAP, int hasGaps) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::scannerParameters(const IBString &xml) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::scannerData(int reqId, int rank, const ContractDetails &contractDetails, const IBString &distance, const IBString &benchmark, const IBString &projection, const IBString &legsStr) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::scannerDataEnd(int reqId) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::realtimeBar(TickerId reqId, long time, double open, double high, double low, double close, long volume, double wap, int count) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::fundamentalData(TickerId reqId, const IBString& data) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::deltaNeutralValidation(int reqId, const UnderComp& underComp) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::tickSnapshotEnd(int reqId) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::marketDataType(TickerId reqId, int marketDataType) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::commissionReport( const CommissionReport& commissionReport) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::position( const IBString& account, const Contract& contract, int position) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::positionEnd() {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::accountSummary( int reqId, const IBString& account, const IBString& tag, const IBString& value, const IBString& curency) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}
void mkdatarobot::accountSummaryEnd( int reqId) {
  //printf("[%s]\n",__PRETTY_FUNCTION__);
}

